package com.dhcc.bpm.common.segmentId;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @description http服务缓存id生成器
 * @author guogang
 * @date 2020年7月15日 上午9:29:07
 * @version 1.0.0
 */
@EnableScheduling
public class HttpCacheIdGenerator {
	private static final Logger log = LoggerFactory.getLogger(HttpCacheIdGenerator.class);
	public static final int RETRY = 10; // 重试次数
	protected volatile SegmentId current;
	protected volatile SegmentId next;
	private volatile boolean isLoadingNext;
	private Object lock = new Object();
	private String bizType = "alarmNo";
	private String address="127.0.0.1";
	RestTemplate restTemplate = new RestTemplate();

	public static final String URI = "/id/nextSegmentId";

	public static final String DATASOURCE_DATE_URL = "/id/getDataSouceSysDate";

	private ExecutorService executorService = Executors.newSingleThreadExecutor();

	public String getBizType() {
		return bizType;
	}

	public void setBizType(String bizType) {
		this.bizType = bizType;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	/**
	 * @description 获取id
	 * @date 2020年7月15日 上午9:31:48
	 * @return 流水id
	 */
	public String nextId() {
		for (int i = 0; i < RETRY; i++) {
			if (current == null) {
				loadCurrent();
				continue;
			}
			Result result = current.nextId();
			if (result.getCode() == ResultCode.OVER) {
				loadCurrent();
			} else {
				if (result.getCode() == ResultCode.LOADING) {
					loadNext();
				}
				return current.getCurDate() + String.format("%0" + current.getLen() + "d", result.getId());
			}
		}
		throw new RuntimeException("获取 nextId 次数超出:" +RETRY+" !");
	}

	/**
	 * @description 加载当前使用分段
	 * @date 2020年7月15日 上午9:31:12
	 */
	public synchronized void loadCurrent() {
		if (current == null || !current.isUsed()) {
			if (next == null) {
				SegmentId segmentId = querySegmentId();
				this.current = segmentId;
			} else {
				current = next;
				next = null;
			}
		}
	}

	/**
	 * @description 查询分段id
	 * @date 2020年7月15日 上午9:30:46
	 * @return
	 */
	private SegmentId querySegmentId() {
		SegmentId segmentId = null;
		try {
			segmentId = restTemplate.getForObject("http://"+address + URI + "?bizType=" + bizType, SegmentId.class);
		} catch (Exception e) {
			log.error("获取分段异常！！！", e);
		}
		return segmentId;
	}

	/**
	 * @description 同步数据库日期 每两分钟同步一次
	 * @date 2020年7月19日 上午9:03:22
	 */
	@Scheduled(cron = "0 0/2 * * * ?")
	void syncDataSourceDate() {
		String newDate = restTemplate.getForObject(address + DATASOURCE_DATE_URL, String.class);
		log.trace("获取数据库日期[{}]", newDate);
		if(current==null) {
			return;
		}
		if (current.getCurDate().compareTo(newDate) >0) {
			log.error("数据库日期回退！！！curDate[{}],newDate[{}]",current.getCurDate(), newDate);
			throw new RuntimeException("数据库日期回退！！！");
		}
		if (current.getCurDate().compareTo(newDate) <0) {
			log.error("current分段日期变动[{}],分段失效", newDate);
			current = null;
		}
		if (next != null && next.getCurDate().compareTo(newDate)<0) {
			log.error("next日期变动[{}],分段失效", newDate);
			next = null;
		}
	}
	/**
	 * @description 加载下一分段id
	 * @date 2020年7月15日 上午9:30:17
	 */
	public void loadNext() {
		if (next == null && !isLoadingNext) {
			synchronized (lock) {
				if (next == null && !isLoadingNext) {
					isLoadingNext = true;
					executorService.submit(new Runnable() {
						@Override
						public void run() {
							try {
								// 无论获取下个segmentId成功与否，都要将isLoadingNext赋值为false
								next = querySegmentId();
							} finally {
								isLoadingNext = false;
							}
						}
					});
				}
			}
		}
	}
}
